# /usr/bin/env python3
# coding=utf-8

import requests
import json
import os
import re
from datetime import datetime
from enum import Enum
import glob
from typing import Dict, List
from string import Template

'''
1. 从swagger-resources获取所有模块的信息
2. 从每个模块的json中获取所有接口的信息
3. 基于接口信息+测试脚本模板，生成对应的初始模块的测试脚本
'''


class ModuleNameMapping(Enum):
    common = 'common'
    business = '业务后台'
    task = '任务模块'
    mall = '商城模块'
    product = '商品模块'
    manage = '管理'
    app = '小程序'


def get_swagger_resource_path(domain: str) -> dict:
    """请求指定URL并返回JSON响应结果"""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
    }

    url = domain + "/swagger-resources"
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()  # 自动处理HTTP错误状态码
        return {
            "status_code": response.status_code,
            "success": True,
            "data": response.json()
        }
    except requests.exceptions.RequestException as e:
        return {
            "status_code": getattr(e.response, 'status_code', 500),
            "success": False,
            "error": f"请求失败: {str(e)}"
        }
    except json.JSONDecodeError:
        return {
            "status_code": 500,
            "success": False,
            "error": "响应内容不是有效的JSON格式"
        }


def get_swagger_module_json(domain: str, module_info: dict):
    """请求指定URL并返回JSON响应结果"""
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
    }
    api = module_info.get("url")
    api_name = module_info.get("name")
    url = domain + api
    try:
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()  # 自动处理HTTP错误状态码
        return {
            "status_code": response.status_code,
            "success": True,
            "data": response.json()
        }
    except requests.exceptions.RequestException as e:
        return {
            "status_code": getattr(e.response, 'status_code', 500),
            "success": False,
            "error": f"请求失败: {str(e)}"
        }
    except json.JSONDecodeError:
        return {
            "status_code": 500,
            "success": False,
            "error": "响应内容不是有效的JSON格式"
        }


def extract_component_by_name(json_file: str, origin_schema_name: str) -> Dict:
    """从OpenAPI格式的JSON文件中提取指定名称的组件"""
    with open(json_file, 'r', encoding='utf-8') as f:
        spec = json.load(f)

    # 从origin_schema_name中提取实际需要的schema名称
    exp_schema_name = origin_schema_name.split('/')[-1]

    # 遍历所有组件，查找指定名称的组件
    components = spec.get('components', {})
    schemas = components.get('schemas', {})
    if exp_schema_name in schemas.keys():
        schema_properties = schemas[exp_schema_name].get('properties', {})
        # print(schema_properties)
        return schema_properties


def extract_endpoints(json_file: str) -> List[Dict]:
    """从OpenAPI格式的JSON文件中提取接口信息"""

    with open(json_file, 'r', encoding='utf-8') as f:
        spec = json.load(f)

    endpoints = []

    # 遍历所有接口路径
    for path, methods in spec.get('paths', {}).items():
        # 遍历每个HTTP方法
        for method, details in methods.items():
            endpoint = {
                'name': details.get('summary', 'Unknown API'),
                'path': path,
                'method': method.upper(),
                'requestBody': [],
                'parameters': [],
                'responses': []
            }

            # 提取请求参数
            for param in details.get('parameters', []):
                endpoint['parameters'].append({
                    'name': param.get('name'),
                    'in': param.get('in'),  # path/query/header/cookie
                    'required': param.get('required', False),
                    'description': param.get('description')
                })

            # 提取Body参数
            if 'requestBody' in details.keys():
                request_body = details['requestBody']
                if 'content' in request_body.keys():
                    content = request_body['content']
                    for content_type, schema in content.items():
                        if 'schema' in schema.keys():
                            # 读取schema的ref信息,如果不存在则可能报错
                            target_schema_name = schema['schema'].get('$ref', '')
                            endpoint['requestBody'] = extract_component_by_name(json_file, target_schema_name)

            # 提取响应信息
            for status_code, response in details.get('responses', {}).items():
                endpoint['responses'].append({
                    'status': status_code,
                    'description': response.get('description'),
                    'content_type': list(response.get('content', {}).keys())
                })

            endpoints.append(endpoint)

    return endpoints


def generate_pytest_script(module_name: str, endpoints: List[Dict]) -> str:
    """生成pytest测试脚本"""
    test_template = Template('''"""
基于Swagger Doc, 通过解析内容生成的接口测试脚本 - $module_name
生成时间：${timestamp}
"""
#/usr/bin/python3
# coding=utf-8

import json
import pytest
from utils.api_assertion import JsonResponseValidator
from utils.data_loader import load_yaml_testdata

class Test${module_name}:
    """${module_name}接口测试类"""

    $test_cases
    ''')

    case_template = Template('''
    @pytest.mark.$case_tag  # 新增标签标记
    def test_${case_name}(self, api_client, case_data=None, **kwargs):
        """$api_name"""
        api_url='$url'
        method = '$method'
        params = None
        data = None

        if case_data:
            params = case_data.get("params", None)
            data = case_data.get("data", None)

        if method.lower() == 'post':
            data = json.dumps(data)

        # 发送接口请求
        response = api_client.send_request(
            method=method,
            endpoint=api_url,
            params=params,
            data=data
        )

        # 断言
        validator = JsonResponseValidator(response.json())
        if case_data:       
            for assertion in case_data["assertions"]:
                if "value_equals" in assertion:
                    validator.key_exists(assertion["key"]).value_equals(assertion["value_equals"])
                elif "value_contains" in assertion:
                    validator.key_exists(assertion["key"]).value_contains(assertion["value_contains"])

        else:
            validator.key_exists("code").value_equals(200)
        # ... 其他断言
''')

    cases = []
    for endpoint in endpoints:
        # 处理URL路径参数
        api_path = endpoint['path']

        # 构造模板数据
        case_data = {
            'case_tag': module_name,  # 新增标签字段
            'case_name': api_path.lstrip('/').replace('/', '_').replace('-', '_'),
            'api_name': endpoint.get('name'),
            'method': endpoint.get('method'),
            'url': api_path,
            'request_body': endpoint.get('requestBody'),
            'params': endpoint.get('parameters'),
            'data': endpoint.get('requestBody', {}),  # 添加data字段的默认值
        }
        cases.append(case_template.substitute(case_data))

    return test_template.substitute(
        module_name=module_name,
        timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        test_cases='\n'.join(cases)
    )


def generate_module_script(module_name: str, endpoints: List[Dict]) -> str:
    """将swagger api转换成python module, 并实现业务串联"""
    test_template = Template('''
"""
自动生成的接口文件 - $module_name
生成时间：${timestamp}
"""
import sys
sys.path.append(".")
sys.path.append("..")
sys.donot_write_bytecode = True

from utils.api_client import APIRequest
from typing import Any, Dict

$test_cases
    ''')

    case_template = Template('''
def ${func_name}(api_client: APIRequest, method='${method}',params=None, payload=None, **kwargs):
    """${api_summary}"""
    url = "${raw_path}"

    # 发送请求
    response = api_client.send_request(
        method=method,
        api_url=url,
        params = params,
        data = payload
    )

    return response
''')

    cases = []
    for endpoint in endpoints:
        # 解析路径参数（如 /users/{id}）
        path_params = re.findall(r'\{(\w+)\}', endpoint['path'])
        raw_path = endpoint['path']

        # 生成方法参数和请求参数映射
        param_sections = []
        request_mappings = []
        path_format_args = []

        # 1. 处理路径参数
        if path_params:
            for p in path_params:
                param_sections.append(f"{p}: str")
                path_format_args.append(f"{p}={p}")
            param_sections.append("")  # 参数换行分隔

        # 2. 处理查询参数 (修正此处逻辑)
        query_params = [p for p in endpoint['parameters'] if p['in'] == 'query']
        query_params_section = "**params: Dict[str, Any]" if query_params else ""
        if query_params:
            param_sections.append(query_params_section)
            request_mappings.append("'params': params")

        # 3. 处理请求体
        body_param_section = "payload: Dict[str, Any]" if endpoint['requestBody'] else ""
        if endpoint['requestBody']:
            param_sections.append(body_param_section)
            request_mappings.append("'json': payload")

        # 构造模板数据（确保包含所有模板变量）
        case_data = {
            'func_name': raw_path.lstrip('/').replace('/', '_'),
            'api_summary': endpoint.get('name', '未知接口'),
            'method': endpoint['method'].lower(),
            'raw_path': raw_path,
            'path_params': ",\n    ".join(param_sections),
            'query_params': query_params_section,  # 确保变量存在
            'body_param': body_param_section,  # 确保变量存在
            'path_format_args': ", ".join(path_format_args),
            'request_params_body': ",\n        ".join(request_mappings),
            'request_arguments': ",\n        ".join(request_mappings)
        }
        cases.append(case_template.substitute(case_data))

    return test_template.substitute(
        module_name=module_name,
        timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        test_cases='\n'.join(cases)
    )


def generate_swagger_api_json_file_entry(domain: str, project_root_path: str):
    result = get_swagger_resource_path(domain)
    if result['success']:
        module_list = result['data']
        # 创建存储所有接口的列表
        all_endpoints = []

        for module_info in module_list:
            # 添加模块信息打印
            print(f"\n正在获取模块: {module_info['name']} ({module_info['url']})")
            module_name = [item.name for item in ModuleNameMapping if item.value == module_info['name']][0]

            module_result = get_swagger_module_json(domain, module_info)
            if module_result['success']:
                # 将获取的API文档保存为临时文件
                temp_file = f"swagger_{module_name}.json"
                api_doc_file = os.path.join(project_root_path, 'api_docs', temp_file)
                print(api_doc_file)
                with open(api_doc_file, 'w', encoding='utf-8') as f:
                    json.dump(module_result['data'], f, ensure_ascii=False)

            else:
                print(f"模块 {module_info['name']} 请求失败: {module_result['error']}")

    else:
        print("请求异常：", result['error'])


def generate_api_script_file_by_name(project_root_path: str, type_name: str):
    # 在文件保存后添加生成测试脚本的逻辑
    output_file = None
    api_docs_dir = os.path.join(project_root_path, 'api_docs')
    module_folder = 'testset' if type_name == 'tests' else 'modules'
    module_dir = os.path.join(project_root_path, module_folder)
    os.makedirs(module_dir, exist_ok=True)

    # 遍历所有临时文件
    for temp_file in glob.glob(os.path.join(api_docs_dir, 'swagger_*.json')):
        module_name = os.path.basename(temp_file).split('_', 1)[1].split('.')[0]

        # 提取接口信息
        endpoints = extract_endpoints(temp_file)

        # 生成测试脚本
        if module_folder == 'testset':
            script_content = generate_pytest_script(module_name, endpoints)
            output_file = os.path.join(module_dir, f'test_{module_name}_api.py')
        else:
            script_content = generate_module_script(module_name, endpoints)
            output_file = os.path.join(module_dir, f'{module_name}_api.py')

        # 保存测试文件
        with open(output_file, 'w', encoding='utf-8') as f:
            f.write(script_content)

        print(f"已生成测试脚本: {output_file}")


if __name__ == "__main__":
    domain = 'https://prod.xiulie-sh.com/api/'
    project_root_path = 'D:/myProjects/Waka/waka_test_project'
    # generate_swagger_api_json_file_entry(domain, project_root_path)
    generate_api_script_file_by_name(project_root_path, 'tests')
    # json_file = os.path.join(project_root_path, 'api_docs', 'swagger_mall.json')
    # extract_endpoints(json_file)

